#include "correlationmatrix.h"

CorrelationMatrix::CorrelationMatrix()
{
	rows_ = new std::vector<DoubleVector>;
}

CorrelationMatrix::~CorrelationMatrix()
{

}

CorrelationMatrix& CorrelationMatrix::operator= (const CorrelationMatrix& m)
{
	rows_ = m.rows_;
	return *this;
}

double CorrelationMatrix::at(unsigned row, unsigned col) const
{
	if (row > height() || col > width() )
		throw "const Matrix subscript out of bounds";
	return rows_->at(row).at(col);
}

void CorrelationMatrix::set(unsigned row, unsigned col, double newval)
{
	if (row >= height() || col >= width() )
		throw "const Matrix subscript out of bounds";
	rows_->at(row).at(col) = newval;
	emit changed(row,col);
}

/*double& CorrelationMatrix::at(unsigned row, unsigned col)
{
if (row >= height() || col >= width())
throw "Matrix subscript out of bounds";
return rows_->at(row).at(col);
}

double& CorrelationMatrix::operator() (unsigned row, unsigned col)
{
if (row >= height() || col >= width())
throw "Matrix subscript out of bounds";
return rows_->at(row).at(col);
//return data_[cols_*row + col];
}

double CorrelationMatrix::operator() (unsigned row, unsigned col) const
{
if (row >= height() || col >= width() )
throw "const Matrix subscript out of bounds";
return rows_->at(row).at(col);
//return data_[cols_*row + col];
}*/

unsigned CorrelationMatrix::height() const
{
	return rows_->size();
}

unsigned CorrelationMatrix::width() const
{
	if(height()>0)
		return rows_->at(0).size();
	else
		return 0;
}

double CorrelationMatrix::minValue()
{
	double min = 999999;
	for(int i = 0;i<(int)rows_->size();i++)
	{
		DoubleVector row = rows_->at(i);
		for(int j = 0;j<(int)row.size();j++)
		{
			double d = row.at(j);
			if(d<min)
				min = d;
		}
	}
	return min;	
}

double CorrelationMatrix::maxValue()
{
	double max = 0;
	for(int i = 0;i<(int)rows_->size();i++)
	{
		DoubleVector row = rows_->at(i);
		for(int j = 0;j<(int)row.size();j++)
		{
			double d = row.at(j);
			double dist = d-1.0;
			if(dist<0)
				dist*=-1.0;
			if(d>max && dist>0.000001) //compensate for double precision (1.0!=0.9999999999999)
				max = d;
		}
	}
	return max;	
}


void CorrelationMatrix::print()
{
	printf("Listing content of matrix:\n");
	for(int i = 0;i<(int)rows_->size();i++)
	{
		DoubleVector row = rows_->at(i);
		for(int j = 0;j<(int)row.size();j++)
		{
			double d = row.at(j);
			printf("%f\t",row.at(j));
		}
		printf("\n");
	}
}

void CorrelationMatrix::addRow(DoubleVector vector)
{
	rows_->push_back(vector);
}

void CorrelationMatrix::loadFromCSVFile(std::string filepath)
{
	rows_->clear();
	try
	{
		char buffer[16384];                     //16kb buffer, just to be sure
		std::ifstream file(filepath.c_str());
		std::vector<DoubleVector> rows;

		while (! file.eof() )
		{
			DoubleVector row;
			rows.push_back(row);
			file.getline(buffer,16384);
			std::stringstream ss(buffer);
			std::string token;
			std::string line(buffer);
			if(line.size() == 0)
				continue;

			while(getline(ss,token,';'))
			{
				const char* str = token.c_str();
				double d = atof(token.c_str());
				row.push_back(d);
			}
			addRow(row);
		}
		if(width() > height()) //make square if last row is left out
			addLastRow();
	}
	catch(std::exception)
	{
	}
}

void CorrelationMatrix::addLastRow()
{
	DoubleVector row;
	for(int i = 0;i < (int)width()-1;i++)
		row.push_back(0.0);

	row.push_back(1.0);

	addRow(row);
}


void CorrelationMatrix::loadFromMATFile(std::string filepath)
{
	rows_->clear();

    mat_t  *mat;
	matvar_t *matvar;

	const char* path = filepath.c_str();

	mat = Mat_Open(path,MAT_ACC_RDONLY);

	Mat_Rewind (mat);

	matvar = Mat_VarReadNext(mat);

	int* dimensions = matvar->dims;
	int numRows = dimensions[0];
	int numCols = dimensions[1];

	double *data = (double*)matvar->data;

	for(int row = 0; row < numRows; row++ ){
		DoubleVector drow;
		for(int col = 0; col < numCols; col++){
			//(a * B * C) + (b * C) + (c * 1)
			double d = data[col*numRows+row];
			drow.push_back(d);
		}
		addRow(drow);
	}

	Mat_Close(mat);

	if(width() > height()) //make square if last row is left out
		addLastRow();
}

CorrelationMatrix* CorrelationMatrix::createFromFile(std::string filepath)
{
	CorrelationMatrix *matrix = new CorrelationMatrix();
	QString path(filepath.c_str());
	if(path.endsWith("csv"))
		matrix->loadFromCSVFile(filepath);
	else
		matrix->loadFromMATFile(filepath);
	return matrix;
}

QVector<CorrelationMatrix*> CorrelationMatrix::createListFromFile(std::string filepath)
{
	QVector<CorrelationMatrix*> matrices;

    mat_t  *mat;
	matvar_t *matvar;

	const char* path = filepath.c_str();

	mat = Mat_Open(path,MAT_ACC_RDONLY);

	Mat_Rewind (mat);

	matvar = Mat_VarReadNext(mat);

	int compression = matvar->compression;

	char* name = matvar->name;

	int* dimensions = matvar->dims;
	int numSubjs = dimensions[0];
	int numRows = dimensions[1];
	int numCols = dimensions[2];

	double *data = (double*)matvar->data;

	for(int subj = 0; subj < numSubjs; subj++)
	{
		CorrelationMatrix *matrix = new CorrelationMatrix();
		for(int row = 0; row < numRows; row++ ){
			DoubleVector drow;
			for(int col = 0; col < numCols; col++){
				//(a * B * C) + (b * C) + (c * 1)
				double d = data[col*numSubjs*numRows+row*numSubjs+subj];
				drow.push_back(d);
			}
			matrix->addRow(drow);
		}
		if(matrix->width() > matrix->height()) //make square if last row is left out
			matrix->addLastRow();

		matrices.push_back(matrix);
	}

	Mat_Close(mat);

	return matrices;
}